2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 // Various useful functions, mainly for formatting c++ code.
29 class ComponentLayout
;
31 //==============================================================================
33 A rectangle whose co-ordinates can be defined in terms of absolute or
34 proportional distances.
36 Designed mainly for storing component positions, this gives you a lot of
37 control over how each co-ordinate is stored, either as an absolute position,
38 or as a proportion of the size of a parent rectangle.
40 It also allows you to define the anchor points by which the rectangle is
41 positioned, so for example you could specify that the top right of the
42 rectangle should be an absolute distance from its parent's bottom-right corner.
44 This object can be stored as a string, which takes the form "x y w h", including
45 symbols like '%' and letters to indicate the anchor point. See its toString()
54 // this will set the child component's x to be 20% of our width, its y
55 // to be 30, its width to be 150, and its height to be 50% of our
57 const PositionedRectangle pos1 ("20% 30 150 50%");
58 pos1.applyToComponent (*myChildComponent1);
60 // this will inset the child component with a gap of 10 pixels
61 // around each of its edges..
62 const PositionedRectangle pos2 ("10 10 20M 20M");
63 pos2.applyToComponent (*myChildComponent2);
68 class PositionedRectangle
71 //==============================================================================
72 /** Creates an empty rectangle with all co-ordinates set to zero.
74 The default anchor point is top-left; the default
76 PositionedRectangle() throw()
77 : x (0.0), y (0.0), w (0.0), h (0.0),
78 xMode (anchorAtLeftOrTop
| absoluteFromParentTopLeft
),
79 yMode (anchorAtLeftOrTop
| absoluteFromParentTopLeft
),
80 wMode (absoluteSize
), hMode (absoluteSize
)
84 /** Initialises a PositionedRectangle from a saved string version.
86 The string must be in the format generated by toString().
88 PositionedRectangle (const String
& stringVersion
) throw()
91 tokens
.addTokens (stringVersion
, false);
93 decodePosString (tokens
[0], xMode
, x
);
94 decodePosString (tokens
[1], yMode
, y
);
95 decodeSizeString (tokens
[2], wMode
, w
);
96 decodeSizeString (tokens
[3], hMode
, h
);
99 /** Creates a copy of another PositionedRectangle. */
100 PositionedRectangle (const PositionedRectangle
& other
) throw()
101 : x (other
.x
), y (other
.y
), w (other
.w
), h (other
.h
),
102 xMode (other
.xMode
), yMode (other
.yMode
),
103 wMode (other
.wMode
), hMode (other
.hMode
)
107 /** Copies another PositionedRectangle. */
108 PositionedRectangle
& operator= (const PositionedRectangle
& other
) throw()
122 //==============================================================================
123 /** Returns a string version of this position, from which it can later be
126 The format is four co-ordinates, "x y w h".
128 - If a co-ordinate is absolute, it is stored as an integer, e.g. "100".
129 - If a co-ordinate is proportional to its parent's width or height, it is stored
130 as a percentage, e.g. "80%".
131 - If the X or Y co-ordinate is relative to the parent's right or bottom edge, the
132 number has "R" appended to it, e.g. "100R" means a distance of 100 pixels from
133 the parent's right-hand edge.
134 - If the X or Y co-ordinate is relative to the parent's centre, the number has "C"
135 appended to it, e.g. "-50C" would be 50 pixels left of the parent's centre.
136 - If the X or Y co-ordinate should be anchored at the component's right or bottom
137 edge, then it has "r" appended to it. So "-50Rr" would mean that this component's
138 right-hand edge should be 50 pixels left of the parent's right-hand edge.
139 - If the X or Y co-ordinate should be anchored at the component's centre, then it
140 has "c" appended to it. So "-50Rc" would mean that this component's
141 centre should be 50 pixels left of the parent's right-hand edge. "40%c" means that
142 this component's centre should be placed 40% across the parent's width.
143 - If it's a width or height that should use the parentSizeMinusAbsolute mode, then
144 the number has "M" appended to it.
146 To reload a stored string, use the constructor that takes a string parameter.
148 const String
toString() const throw()
151 s
.preallocateBytes (32);
152 addPosDescription (s
, xMode
, x
); s
<< ' ';
153 addPosDescription (s
, yMode
, y
); s
<< ' ';
154 addSizeDescription (s
, wMode
, w
); s
<< ' ';
155 addSizeDescription (s
, hMode
, h
);
159 //==============================================================================
160 /** Calculates the absolute position, given the size of the space that
163 This will work out any proportional distances and sizes relative to the
164 target rectangle, and will return the absolute position.
166 @see applyToComponent
168 const Rectangle
<int> getRectangle (const Rectangle
<int>& target
) const throw()
170 jassert (! target
.isEmpty());
172 double x_
, y_
, w_
, h_
;
173 applyPosAndSize (x_
, w_
, x
, w
, xMode
, wMode
, target
.getX(), target
.getWidth());
174 applyPosAndSize (y_
, h_
, y
, h
, yMode
, hMode
, target
.getY(), target
.getHeight());
175 return Rectangle
<int> (roundToInt (x_
), roundToInt (y_
), roundToInt (w_
), roundToInt (h_
));
178 /** Same as getRectangle(), but returning the values as doubles rather than ints. */
179 void getRectangleDouble (const Rectangle
<int>& target
,
180 double& x_
, double& y_
, double& w_
, double& h_
) const throw()
182 jassert (! target
.isEmpty());
183 applyPosAndSize (x_
, w_
, x
, w
, xMode
, wMode
, target
.getX(), target
.getWidth());
184 applyPosAndSize (y_
, h_
, y
, h
, yMode
, hMode
, target
.getY(), target
.getHeight());
187 /** This sets the bounds of the given component to this position.
189 This is equivalent to writing:
191 comp.setBounds (getRectangle (Rectangle<int> (0, 0, comp.getParentWidth(), comp.getParentHeight())));
194 @see getRectangle, updateFromComponent
196 void applyToComponent (Component
& comp
) const throw()
198 comp
.setBounds (getRectangle (Rectangle
<int> (comp
.getParentWidth(), comp
.getParentHeight())));
201 //==============================================================================
202 /** Updates this object's co-ordinates to match the given rectangle.
204 This will set all co-ordinates based on the given rectangle, re-calculating
205 any proportional distances, and using the current anchor points.
207 So for example if the x co-ordinate mode is currently proportional, this will
208 re-calculate x based on the rectangle's relative position within the target
211 If the target rectangle's width or height are zero then it may not be possible
212 to re-calculate some proportional co-ordinates. In this case, those co-ordinates
215 void updateFrom (const Rectangle
<int>& newPosition
,
216 const Rectangle
<int>& targetSpaceToBeRelativeTo
) throw()
218 updatePosAndSize (x
, w
, newPosition
.getX(), newPosition
.getWidth(), xMode
, wMode
, targetSpaceToBeRelativeTo
.getX(), targetSpaceToBeRelativeTo
.getWidth());
219 updatePosAndSize (y
, h
, newPosition
.getY(), newPosition
.getHeight(), yMode
, hMode
, targetSpaceToBeRelativeTo
.getY(), targetSpaceToBeRelativeTo
.getHeight());
222 /** Same functionality as updateFrom(), but taking doubles instead of ints.
224 void updateFromDouble (const double newX
, const double newY
,
225 const double newW
, const double newH
,
226 const Rectangle
<int>& target
) throw()
228 updatePosAndSize (x
, w
, newX
, newW
, xMode
, wMode
, target
.getX(), target
.getWidth());
229 updatePosAndSize (y
, h
, newY
, newH
, yMode
, hMode
, target
.getY(), target
.getHeight());
232 /** Updates this object's co-ordinates to match the bounds of this component.
234 This is equivalent to calling updateFrom() with the component's bounds and
237 If the component doesn't currently have a parent, then proportional co-ordinates
238 might not be updated because it would need to know the parent's size to do the
241 void updateFromComponent (const Component
& comp
) throw()
243 if (comp
.getParentComponent() == 0 && ! comp
.isOnDesktop())
244 updateFrom (comp
.getBounds(), Rectangle
<int>());
246 updateFrom (comp
.getBounds(), Rectangle
<int> (comp
.getParentWidth(), comp
.getParentHeight()));
249 //==============================================================================
250 /** Specifies the point within the rectangle, relative to which it should be positioned. */
253 anchorAtLeftOrTop
= 1 << 0, /**< The x or y co-ordinate specifies where the left or top edge of the rectangle should be. */
254 anchorAtRightOrBottom
= 1 << 1, /**< The x or y co-ordinate specifies where the right or bottom edge of the rectangle should be. */
255 anchorAtCentre
= 1 << 2 /**< The x or y co-ordinate specifies where the centre of the rectangle should be. */
258 /** Specifies how an x or y co-ordinate should be interpreted. */
261 absoluteFromParentTopLeft
= 1 << 3, /**< The x or y co-ordinate specifies an absolute distance from the parent's top or left edge. */
262 absoluteFromParentBottomRight
= 1 << 4, /**< The x or y co-ordinate specifies an absolute distance from the parent's bottom or right edge. */
263 absoluteFromParentCentre
= 1 << 5, /**< The x or y co-ordinate specifies an absolute distance from the parent's centre. */
264 proportionOfParentSize
= 1 << 6 /**< The x or y co-ordinate specifies a proportion of the parent's width or height, measured from the parent's top or left. */
267 /** Specifies how the width or height should be interpreted. */
270 absoluteSize
= 1 << 0, /**< The width or height specifies an absolute size. */
271 parentSizeMinusAbsolute
= 1 << 1, /**< The width or height is an amount that should be subtracted from the parent's width or height. */
272 proportionalSize
= 1 << 2, /**< The width or height specifies a proportion of the parent's width or height. */
275 //==============================================================================
276 /** Sets all options for all co-ordinates.
278 This requires a reference rectangle to be specified, because if you're changing any
279 of the modes from proportional to absolute or vice-versa, then it'll need to convert
280 the co-ordinates, and will need to know the parent size so it can calculate this.
282 void setModes (const AnchorPoint xAnchor
, const PositionMode xMode_
,
283 const AnchorPoint yAnchor
, const PositionMode yMode_
,
284 const SizeMode widthMode
, const SizeMode heightMode
,
285 const Rectangle
<int>& target
) throw()
287 if (xMode
!= (xAnchor
| xMode_
) || wMode
!= widthMode
)
290 applyPosAndSize (tx
, tw
, x
, w
, xMode
, wMode
, target
.getX(), target
.getWidth());
292 xMode
= (uint8
) (xAnchor
| xMode_
);
293 wMode
= (uint8
) widthMode
;
295 updatePosAndSize (x
, w
, tx
, tw
, xMode
, wMode
, target
.getX(), target
.getWidth());
298 if (yMode
!= (yAnchor
| yMode_
) || hMode
!= heightMode
)
301 applyPosAndSize (ty
, th
, y
, h
, yMode
, hMode
, target
.getY(), target
.getHeight());
303 yMode
= (uint8
) (yAnchor
| yMode_
);
304 hMode
= (uint8
) heightMode
;
306 updatePosAndSize (y
, h
, ty
, th
, yMode
, hMode
, target
.getY(), target
.getHeight());
310 /** Returns the anchoring mode for the x co-ordinate.
311 To change any of the modes, use setModes().
313 AnchorPoint
getAnchorPointX() const throw()
315 return (AnchorPoint
) (xMode
& (anchorAtLeftOrTop
| anchorAtRightOrBottom
| anchorAtCentre
));
318 /** Returns the positioning mode for the x co-ordinate.
319 To change any of the modes, use setModes().
321 PositionMode
getPositionModeX() const throw()
323 return (PositionMode
) (xMode
& (absoluteFromParentTopLeft
| absoluteFromParentBottomRight
324 | absoluteFromParentCentre
| proportionOfParentSize
));
327 /** Returns the raw x co-ordinate.
329 If the x position mode is absolute, then this will be the absolute value. If it's
330 proportional, then this will be a fractional proportion, where 1.0 means the full
331 width of the parent space.
333 double getX() const throw() { return x
; }
335 /** Sets the raw value of the x co-ordinate.
337 See getX() for the meaning of this value.
339 void setX (const double newX
) throw() { x
= newX
; }
341 /** Returns the anchoring mode for the y co-ordinate.
342 To change any of the modes, use setModes().
344 AnchorPoint
getAnchorPointY() const throw()
346 return (AnchorPoint
) (yMode
& (anchorAtLeftOrTop
| anchorAtRightOrBottom
| anchorAtCentre
));
349 /** Returns the positioning mode for the y co-ordinate.
350 To change any of the modes, use setModes().
352 PositionMode
getPositionModeY() const throw()
354 return (PositionMode
) (yMode
& (absoluteFromParentTopLeft
| absoluteFromParentBottomRight
355 | absoluteFromParentCentre
| proportionOfParentSize
));
358 /** Returns the raw y co-ordinate.
360 If the y position mode is absolute, then this will be the absolute value. If it's
361 proportional, then this will be a fractional proportion, where 1.0 means the full
362 height of the parent space.
364 double getY() const throw() { return y
; }
366 /** Sets the raw value of the y co-ordinate.
368 See getY() for the meaning of this value.
370 void setY (const double newY
) throw() { y
= newY
; }
372 /** Returns the mode used to calculate the width.
373 To change any of the modes, use setModes().
375 SizeMode
getWidthMode() const throw() { return (SizeMode
) wMode
; }
377 /** Returns the raw width value.
379 If the width mode is absolute, then this will be the absolute value. If the mode is
380 proportional, then this will be a fractional proportion, where 1.0 means the full
381 width of the parent space.
383 double getWidth() const throw() { return w
; }
385 /** Sets the raw width value.
387 See getWidth() for the details about what this value means.
389 void setWidth (const double newWidth
) throw() { w
= newWidth
; }
391 /** Returns the mode used to calculate the height.
392 To change any of the modes, use setModes().
394 SizeMode
getHeightMode() const throw() { return (SizeMode
) hMode
; }
396 /** Returns the raw height value.
398 If the height mode is absolute, then this will be the absolute value. If the mode is
399 proportional, then this will be a fractional proportion, where 1.0 means the full
400 height of the parent space.
402 double getHeight() const throw() { return h
; }
404 /** Sets the raw height value.
406 See getHeight() for the details about what this value means.
408 void setHeight (const double newHeight
) throw() { h
= newHeight
; }
410 //==============================================================================
411 /** If the size and position are constance, and wouldn't be affected by changes
412 in the parent's size, then this will return true.
414 bool isPositionAbsolute() const throw()
416 return xMode
== absoluteFromParentTopLeft
417 && yMode
== absoluteFromParentTopLeft
418 && wMode
== absoluteSize
419 && hMode
== absoluteSize
;
422 //==============================================================================
423 /** Compares two objects. */
424 bool operator== (const PositionedRectangle
& other
) const throw()
426 return x
== other
.x
&& y
== other
.y
427 && w
== other
.w
&& h
== other
.h
428 && xMode
== other
.xMode
&& yMode
== other
.yMode
429 && wMode
== other
.wMode
&& hMode
== other
.hMode
;
432 /** Compares two objects. */
433 bool operator!= (const PositionedRectangle
& other
) const throw()
435 return ! operator== (other
);
439 //==============================================================================
441 uint8 xMode
, yMode
, wMode
, hMode
;
443 void addPosDescription (String
& s
, const uint8 mode
, const double value
) const throw()
445 if ((mode
& proportionOfParentSize
) != 0)
447 s
<< (roundToInt (value
* 100000.0) / 1000.0) << '%';
451 s
<< (roundToInt (value
* 100.0) / 100.0);
453 if ((mode
& absoluteFromParentBottomRight
) != 0)
455 else if ((mode
& absoluteFromParentCentre
) != 0)
459 if ((mode
& anchorAtRightOrBottom
) != 0)
461 else if ((mode
& anchorAtCentre
) != 0)
465 void addSizeDescription (String
& s
, const uint8 mode
, const double value
) const throw()
467 if (mode
== proportionalSize
)
468 s
<< (roundToInt (value
* 100000.0) / 1000.0) << '%';
469 else if (mode
== parentSizeMinusAbsolute
)
470 s
<< (roundToInt (value
* 100.0) / 100.0) << 'M';
472 s
<< (roundToInt (value
* 100.0) / 100.0);
475 void decodePosString (const String
& s
, uint8
& mode
, double& value
) throw()
477 if (s
.containsChar ('r'))
478 mode
= anchorAtRightOrBottom
;
479 else if (s
.containsChar ('c'))
480 mode
= anchorAtCentre
;
482 mode
= anchorAtLeftOrTop
;
484 if (s
.containsChar ('%'))
486 mode
|= proportionOfParentSize
;
487 value
= s
.removeCharacters ("%rcRC").getDoubleValue() / 100.0;
491 if (s
.containsChar ('R'))
492 mode
|= absoluteFromParentBottomRight
;
493 else if (s
.containsChar ('C'))
494 mode
|= absoluteFromParentCentre
;
496 mode
|= absoluteFromParentTopLeft
;
498 value
= s
.removeCharacters ("rcRC").getDoubleValue();
502 void decodeSizeString (const String
& s
, uint8
& mode
, double& value
) throw()
504 if (s
.containsChar ('%'))
506 mode
= proportionalSize
;
507 value
= s
.upToFirstOccurrenceOf ("%", false, false).getDoubleValue() / 100.0;
509 else if (s
.containsChar ('M'))
511 mode
= parentSizeMinusAbsolute
;
512 value
= s
.getDoubleValue();
517 value
= s
.getDoubleValue();
521 void applyPosAndSize (double& xOut
, double& wOut
, const double x_
, const double w_
,
522 const uint8 xMode_
, const uint8 wMode_
,
523 const int parentPos
, const int parentSize
) const throw()
525 if (wMode_
== proportionalSize
)
526 wOut
= roundToInt (w_
* parentSize
);
527 else if (wMode_
== parentSizeMinusAbsolute
)
528 wOut
= jmax (0, parentSize
- roundToInt (w_
));
530 wOut
= roundToInt (w_
);
532 if ((xMode_
& proportionOfParentSize
) != 0)
533 xOut
= parentPos
+ x_
* parentSize
;
534 else if ((xMode_
& absoluteFromParentBottomRight
) != 0)
535 xOut
= (parentPos
+ parentSize
) - x_
;
536 else if ((xMode_
& absoluteFromParentCentre
) != 0)
537 xOut
= x_
+ (parentPos
+ parentSize
/ 2);
539 xOut
= x_
+ parentPos
;
541 if ((xMode_
& anchorAtRightOrBottom
) != 0)
543 else if ((xMode_
& anchorAtCentre
) != 0)
547 void updatePosAndSize (double& xOut
, double& wOut
, double x_
, const double w_
,
548 const uint8 xMode_
, const uint8 wMode_
,
549 const int parentPos
, const int parentSize
) const throw()
551 if (wMode_
== proportionalSize
)
554 wOut
= w_
/ parentSize
;
556 else if (wMode_
== parentSizeMinusAbsolute
)
557 wOut
= parentSize
- w_
;
561 if ((xMode_
& anchorAtRightOrBottom
) != 0)
563 else if ((xMode_
& anchorAtCentre
) != 0)
566 if ((xMode_
& proportionOfParentSize
) != 0)
569 xOut
= (x_
- parentPos
) / parentSize
;
571 else if ((xMode_
& absoluteFromParentBottomRight
) != 0)
572 xOut
= (parentPos
+ parentSize
) - x_
;
573 else if ((xMode_
& absoluteFromParentCentre
) != 0)
574 xOut
= x_
- (parentPos
+ parentSize
/ 2);
576 xOut
= x_
- parentPos
;
580 //==============================================================================
581 struct RelativePositionedRectangle
583 //==============================================================================
584 PositionedRectangle rect
;
590 //==============================================================================
591 RelativePositionedRectangle();
592 RelativePositionedRectangle (const RelativePositionedRectangle
&);
593 RelativePositionedRectangle
& operator= (const RelativePositionedRectangle
&);
594 ~RelativePositionedRectangle();
596 //==============================================================================
597 bool operator== (const RelativePositionedRectangle
& other
) const throw();
598 bool operator!= (const RelativePositionedRectangle
& other
) const throw();
600 const Rectangle
<int> getRectangle (const Rectangle
<int>& parentArea
, const ComponentLayout
* layout
) const;
601 void getRectangleDouble (double& x
, double& y
, double& w
, double& h
,
602 const Rectangle
<int>& parentArea
, const ComponentLayout
* layout
) const;
603 void updateFromComponent (const Component
& comp
, const ComponentLayout
* layout
);
604 void updateFrom (double newX
, double newY
, double newW
, double newH
,
605 const Rectangle
<int>& parentArea
, const ComponentLayout
* layout
);
607 void applyToXml (XmlElement
& e
) const;
608 void restoreFromXml (const XmlElement
& e
, const RelativePositionedRectangle
& defaultPos
);
610 void getRelativeTargetBounds (const Rectangle
<int>& parentArea
,
611 const ComponentLayout
* layout
,
612 int& x
, int& xw
, int& y
, int& yh
, int& w
, int& h
) const;
615 //==============================================================================
616 const String
replaceCEscapeChars (const String
& s
);
617 const String
makeValidCppIdentifier (String s
,
618 const bool capitalise
,
619 const bool removeColons
,
620 const bool allowTemplates
);
622 const String
quotedString (const String
& s
);
623 // replaces any recognised embedded strings like %%getName()%% ready for display on screen
624 const String
replaceStringTranslations (String s
, JucerDocument
* document
);
626 const String
valueToFloat (const double v
);
627 const String
castToFloat (const String
& expression
);
628 const String
boolToString (const bool b
);
629 const String
justificationToCode (const Justification
& justification
);
631 //==============================================================================
632 const String
indentCode (const String
& code
, const int numSpaces
);
634 int indexOfLineStartingWith (const StringArray
& lines
, const String
& text
, int startIndex
);
636 //==============================================================================
637 const String
colourToHex (const Colour
& col
);
638 const String
colourToCode (const Colour
& col
);
640 void setColourXml (XmlElement
& xml
, const char* const attName
, const Colour
& colour
);
641 const Colour
getColourXml (const XmlElement
& xml
, const char* const attName
, const Colour
& defaultColour
);
643 //==============================================================================
644 const String
positionToString (const RelativePositionedRectangle
& pos
);
646 void positionToXY (const RelativePositionedRectangle
& position
,
647 double& x
, double& y
,
648 const Rectangle
<int>& parentArea
,
649 const ComponentLayout
* layout
);
651 void positionToCode (const RelativePositionedRectangle
& position
,
652 const ComponentLayout
* layout
,
653 String
& x
, String
& y
, String
& w
, String
& h
);
655 //==============================================================================
656 void drawResizableBorder (Graphics
& g
,
658 const BorderSize
<int> borderSize
,
659 const bool isMouseOver
);
661 void drawMouseOverCorners (Graphics
& g
, int w
, int h
);